Skip to content

Conversation

@chrstnb
Copy link
Collaborator

@chrstnb chrstnb commented Nov 24, 2025

Summary

Allow users to configure settings at either the workspace or user-level, with user-level being the default. If a user wants to set a per-workspace setting, it will be set in cwd/.env if non-sensitive and in keychainExtensionPath+cwd if sensitive. Workspace-level settings will take precedence over user-level settings.

Details

Related Issues

How to Validate

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

@chrstnb chrstnb requested a review from a team as a code owner November 24, 2025 17:27
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @chrstnb, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the flexibility of extension settings by introducing user and workspace-level configuration options. This allows developers to maintain global preferences while also enabling project-specific overrides, improving the adaptability of extensions to diverse development environments. The changes include updates to CLI commands and underlying logic to support this new scoping behavior.

Highlights

  • User and Workspace Scoped Settings: Introduced support for configuring extension settings at either a user-level (default) or a workspace-level. Workspace settings take precedence over user settings.
  • Updated gemini extensions settings set command: The set command now includes an optional --scope flag, allowing users to explicitly specify whether a setting should be applied globally (user) or to the current project (workspace).
  • Enhanced gemini extensions settings list command: The list command now displays the scope (user or workspace) for each setting, providing clarity on where a particular configuration originates.
  • Refactored Setting Management Logic: Internal functions for retrieving and updating settings have been refactored to handle the new scoping mechanism, ensuring sensitive data is stored securely in the keychain and non-sensitive data in .env files, specific to their scope.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for user-scoped and workspace-scoped extension settings, which is a great enhancement. The implementation correctly introduces the concept of scopes for both file-based settings and sensitive secrets in the keychain, and correctly handles the precedence of workspace settings over user settings. However, I've found a critical issue in the updateSetting function that could lead to data loss in users' .env files by deleting any variables not managed by the extension. I've also identified a couple of significant issues in the test suite where the tests are not correctly validating the implementation, including one that fails to catch the data loss bug, and another that doesn't properly test the isolation of secrets between scopes. Addressing these issues is crucial before merging.

Comment on lines +535 to 565
it('should merge user and workspace settings, with workspace taking precedence', async () => {
// User settings
const userEnvPath = path.join(extensionDir, EXTENSION_SETTINGS_FILENAME);
await fsPromises.writeFile(
userEnvPath,
'VAR1=user-value1\nVAR3=user-value3',
);
const userKeychain = new KeychainTokenStorage(
`Gemini CLI Extensions test-ext ${extensionId}`,
);
await userKeychain.setSecret('VAR2', 'user-secret2');

// Workspace settings
const workspaceEnvPath = path.join(
tempWorkspaceDir,
EXTENSION_SETTINGS_FILENAME,
);
await fsPromises.writeFile(workspaceEnvPath, 'VAR1=workspace-value1');
const workspaceKeychain = new KeychainTokenStorage(
`Gemini CLI Extensions test-ext ${extensionId} ${tempWorkspaceDir}`,
);
await workspaceKeychain.setSecret('VAR2', 'workspace-secret2');

const contents = await getEnvContents(config, extensionId);

expect(contents).toEqual({
VAR1: 'workspace-value1',
VAR2: 'workspace-secret2',
VAR3: 'user-value3',
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This test case is intended to validate that workspace settings take precedence over user settings, including for sensitive values stored in the keychain. However, the underlying mock for KeychainTokenStorage is flawed, as it uses a single, shared data store for all instances, regardless of the serviceName that differentiates user and workspace keychains. This means the test doesn't correctly verify the isolation of secrets. The test passes coincidentally because the workspace secret is set after the user secret, overwriting it in the shared mock data. To fix this, the KeychainTokenStorage mock should be updated to partition its data based on the serviceName to ensure user and workspace secrets are stored and retrieved independently.

Comment on lines +657 to 688
it('should leave existing, unmanaged .env variables intact when updating in WORKSPACE scope', async () => {
// Setup a pre-existing .env file in the workspace with unmanaged variables
const workspaceEnvPath = path.join(tempWorkspaceDir, '.env');
const originalEnvContent =
'PROJECT_VAR_1=value_1\nPROJECT_VAR_2=value_2\nVAR1=original-value'; // VAR1 is managed by extension
await fsPromises.writeFile(workspaceEnvPath, originalEnvContent);

it('should wrap values with spaces in quotes', async () => {
mockRequestSetting.mockResolvedValue('a value with spaces');
// Simulate updating an extension-managed non-sensitive setting
mockRequestSetting.mockResolvedValue('updated-value');
await updateSetting(
config,
'12345',
'VAR1',
mockRequestSetting,
ExtensionSettingScope.WORKSPACE,
);

await updateSetting(config, '12345', 'VAR1', mockRequestSetting);
// Read the .env file after update
const actualContent = await fsPromises.readFile(
workspaceEnvPath,
'utf-8',
);

const expectedEnvPath = path.join(extensionDir, '.env');
const actualContent = await fsPromises.readFile(expectedEnvPath, 'utf-8');
expect(actualContent).toContain('VAR1="a value with spaces"');
// Assert that original variables are intact and extension variable is updated
expect(actualContent).toContain('PROJECT_VAR_1=value_1');
expect(actualContent).toContain('PROJECT_VAR_2=value_2');
expect(actualContent).toContain('VAR1=updated-value');

// Ensure no other unexpected changes or deletions
const lines = actualContent.split('\n').filter((line) => line.length > 0);
expect(lines).toHaveLength(3); // Should only have the three variables
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This test case is intended to verify that unmanaged variables in a .env file are preserved when an extension setting is updated. However, this test is flawed as it should be failing with the current implementation of updateSetting. The updateSetting function incorrectly overwrites the entire .env file, which would delete the PROJECT_VAR_1 and PROJECT_VAR_2 variables. The test's assertions that these variables are still present should fail. This indicates a potential issue with the test setup or how its assertions are being evaluated. After fixing the data loss bug in updateSetting, this test will be essential for verifying the correct behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant